import mqtt from "mqtt/dist/mqtt";
import { isRef, onBeforeUnmount, readonly, ref, unref, watch } from "vue";

const status = ref("idle");
const currentValue = ref(0);
const lastUpdated = ref(null);
const history = ref([]);

let client = null;
let activeConsumers = 0;
let currentConfig = null;
let historyLimit = 120;

const textDecoder =
  typeof TextDecoder !== "undefined" ? new TextDecoder("utf-8") : null;

const DEFAULT_CONFIG = {
  url:
    typeof import.meta !== "undefined" && import.meta.env.VITE_METHANE_MQTT_URL
      ? import.meta.env.VITE_METHANE_MQTT_URL
      : "ws://localhost:8083/mqtt",
  topic:
    typeof import.meta !== "undefined" &&
    import.meta.env.VITE_METHANE_MQTT_TOPIC
      ? import.meta.env.VITE_METHANE_MQTT_TOPIC
      : "sensors/methane",
  historySize: 120,
  valueKeys: ["concentration", "value", "methane"],
  valueExtractor: null,
  options:
    typeof import.meta !== "undefined" &&
    import.meta.env.VITE_METHANE_MQTT_USERNAME
      ? {
          username: import.meta.env.VITE_METHANE_MQTT_USERNAME,
          password: import.meta.env.VITE_METHANE_MQTT_PASSWORD,
        }
      : undefined,
};

const dataListeners = new Set();

function resolveConfig(config = {}) {
  const { onData: _ignored, ...rest } = config || {};

  const merged = {
    ...DEFAULT_CONFIG,
    ...rest,
  };

  merged.historySize =
    typeof merged.historySize === "number" && merged.historySize > 0
      ? merged.historySize
      : DEFAULT_CONFIG.historySize;

  merged.valueKeys =
    Array.isArray(rest.valueKeys) && rest.valueKeys.length
      ? rest.valueKeys.map((key) => String(key || "").trim()).filter(Boolean)
      : DEFAULT_CONFIG.valueKeys;

  merged.valueExtractor =
    typeof rest.valueExtractor === "function"
      ? rest.valueExtractor
      : DEFAULT_CONFIG.valueExtractor;

  historyLimit = merged.historySize;

  return merged;
}

function cleanupClient() {
  if (!client) return;

  client.removeListener("message", handleMessage);
  client.removeListener("connect", handleConnect);
  client.removeListener("reconnect", handleReconnect);
  client.removeListener("close", handleClose);
  client.removeListener("error", handleError);
  client.end(true);
  client = null;
  status.value = "disconnected";
}

function ensureClient(config = {}) {
  const merged = resolveConfig(config);

  const requiresReconnect = 
    !client ||
    !currentConfig ||
    currentConfig.url !== merged.url ||
    currentConfig.topic !== merged.topic;

  currentConfig = merged;

  if (!requiresReconnect) {
    return;
  }

  if (client) {
    cleanupClient();
  }

  // 检查 URL 是否有效，如果没有配置有效的 URL，则不尝试连接
  if (!merged.url || merged.url === 'undefined') {
    status.value = "idle";
    console.warn("MQTT URL 未配置，跳过连接");
    return;
  }

  status.value = "connecting";

  try {
    client = mqtt.connect(merged.url, {
      ...(merged.options || {}),
      connectTimeout: 5000, // 5秒连接超时
      reconnectPeriod: 10000, // 初始重连间隔10秒
      maxReconnectAttempts: 3 // 最多尝试重连3次
    });
  } catch (error) {
    status.value = "error";
    console.warn("初始化 MQTT 客户端失败", error.message);
    return;
  }

  client.on("connect", handleConnect);
  client.on("reconnect", handleReconnect);
  client.on("close", handleClose);
  client.on("error", handleError);
  client.on("message", handleMessage);

  if (merged.topic) {
    client.subscribe(merged.topic, (err) => {
      if (err) {
        status.value = "error";
        console.error("Failed to subscribe methane topic", err);
      }
    });
  }
}

function handleConnect(message) {
  console.log("Methane MQTT client connected", message);
  status.value = "connected";
}

function handleReconnect() {
  status.value = "reconnecting";
}

function handleClose() {
  status.value = "disconnected";
}

function handleError(error) {
  status.value = "error";
  console.warn("Methane MQTT client error (MQTT 服务可能未启动或配置错误)", error.message);
  // 避免在错误状态下频繁重连
  if (client) {
    client.options.reconnectPeriod = 60000; // 错误后1分钟才尝试重连
  }
}

function parsePayload(message) {
  const text = normaliseMessage(message);

  if (!text) {
    return null;
  }

  const directValue = Number.parseFloat(text);

  if (!Number.isNaN(directValue)) {
    return directValue;
  }

  let payload;
  try {
    payload = JSON.parse(text);
  } catch (error) {
    console.warn("Unable to parse methane payload", text);
    return null;
  }

  if (typeof currentConfig?.valueExtractor === "function") {
    try {
      const extracted = currentConfig.valueExtractor(payload);
      if (isNumericValue(extracted)) {
        return extracted;
      }
    } catch (error) {
      console.error("Methane value extractor failed", error);
    }
  }

  if (isNumericValue(payload)) {
    return payload;
  }

  if (payload && typeof payload === "object") {
    const fromKeys = extractValueFromKeys(payload, currentConfig?.valueKeys);
    if (isNumericValue(fromKeys)) {
      return fromKeys;
    }

    const nested = findFirstNumeric(payload);
    if (isNumericValue(nested)) {
      return nested;
    }
  }

  return null;
}

function handleMessage(_topic, message) {
  const value = JSON.parse(message.toString());
  if (value === null || Number.isNaN(value)) {
    return;
  }
  currentValue.value = value.mmol;
  const timestamp = value.ts;
  lastUpdated.value = timestamp;
  if (history.value.length >= 30) {
    history.value.shift();
  }
  history.value = [...history.value, { timestamp, value: currentValue.value }];
}

function maybeTeardown() {
  if (activeConsumers <= 0) {
    cleanupClient();
  }
}

export function useMethaneMonitor(config = {}) {
  activeConsumers += 1;

  const initialConfig = unref(config) || {};
  const { onData, ...clientConfig } = initialConfig;

  ensureClient(clientConfig);

  const stopListener =
    typeof onData === "function" ? registerListener(onData) : () => {};

  onBeforeUnmount(() => {
    stopListener();
    activeConsumers -= 1;
    maybeTeardown();
  });

  if (isRef(config)) {
    watch(
      config,
      (value) => {
        ensureClient((value && { ...value, onData: undefined }) || {});
      },
      { deep: true }
    );
  }

  return {
    status: readonly(status),
    currentValue: readonly(currentValue),
    lastUpdated: readonly(lastUpdated),
    history: readonly(history),
    reconnect: () => ensureClient(currentConfig || DEFAULT_CONFIG),
  };
}

function registerListener(listener) {
  dataListeners.add(listener);
  return () => dataListeners.delete(listener);
}

export function disconnectMethaneMonitor() {
  activeConsumers = 0;
  cleanupClient();
}

function isNumericValue(value) {
  return typeof value === "number" && Number.isFinite(value);
}

function extractValueFromKeys(payload, keys = []) {
  if (!payload || typeof payload !== "object") return null;
  if (!Array.isArray(keys) || !keys.length) return null;

  for (const rawKey of keys) {
    const key = String(rawKey || "").trim();
    if (!key) continue;

    const segments = key.split(".");
    let cursor = payload;
    let matched = true;

    for (const segment of segments) {
      if (cursor == null) {
        matched = false;
        break;
      }
      const lowerSegment = segment.toLowerCase();
      const nextKey = Object.keys(cursor).find(
        (k) => k.toLowerCase() === lowerSegment
      );
      if (!nextKey) {
        matched = false;
        break;
      }
      cursor = cursor[nextKey];
    }

    if (matched && isNumericValue(cursor)) {
      return cursor;
    }
  }

  return null;
}

function findFirstNumeric(value, depth = 0) {
  if (depth > 4) return null;

  if (Array.isArray(value)) {
    for (const item of value) {
      const numeric = findFirstNumeric(item, depth + 1);
      if (isNumericValue(numeric)) {
        return numeric;
      }
    }
    return null;
  }

  if (value && typeof value === "object") {
    for (const key of Object.keys(value)) {
      const numeric = findFirstNumeric(value[key], depth + 1);
      if (isNumericValue(numeric)) {
        return numeric;
      }
    }
  }

  return isNumericValue(value) ? value : null;
}

function decodeBytes(bytes) {
  if (!bytes) return "";

  if (textDecoder) {
    try {
      return textDecoder.decode(bytes);
    } catch (error) {
      console.warn("TextDecoder failed for methane payload", error);
    }
  }

  if (typeof Buffer !== "undefined" && typeof Buffer.from === "function") {
    try {
      return Buffer.from(bytes).toString("utf-8");
    } catch (error) {
      console.warn("Buffer decoding failed for methane payload", error);
    }
  }

  let result = "";
  for (let i = 0; i < bytes.length; i += 1) {
    result += String.fromCharCode(bytes[i]);
  }
  return result;
}

function normaliseMessage(message) {
  if (message == null) {
    return "";
  }

  if (typeof message === "string") {
    return message;
  }

  if (message instanceof Uint8Array) {
    return decodeBytes(message);
  }

  if (
    Array.isArray(message) &&
    message.every((item) => typeof item === "number")
  ) {
    return decodeBytes(Uint8Array.from(message));
  }

  if (
    typeof message === "object" &&
    "data" in message &&
    Array.isArray(message.data) &&
    message.data.every((item) => typeof item === "number")
  ) {
    return decodeBytes(Uint8Array.from(message.data));
  }

  if (typeof message.toString === "function") {
    const asString = message.toString();
    if (asString && asString !== "[object Object]") {
      return asString;
    }
  }

  try {
    return JSON.stringify(message);
  } catch (error) {
    console.warn("Unable to serialise methane payload", error);
  }

  return "";
}
